using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Omu.ValueInjecter;
using Wolfpack.Periscope.Core.Interfaces;
using Wolfpack.Periscope.Core.Interfaces.Entities;
using Wolfpack.Periscope.Core.Interfaces.Messages;
using Wolfpack.Periscope.Core.Widgets;

namespace Wolfpack.Periscope.Core.Repositories
{
    public class FileSystemDashboardRepository : IDashboardConfigurationRepository
    {
        private readonly object _syncLock = new object();
        private readonly string _folder;

        public FileSystemDashboardRepository()
        {
            _folder = "config";
        }

        public DashboardConfiguration Load()
        {
            EstablishConfiguration();

            lock (_syncLock)
            {
                var panels = Directory.GetDirectories(GetPanelsFolder())
                    .Select(x =>
                    {
                        var panel = LoadPanel(x);
                        var layout = LoadLayout(x);
                        var widgets = LoadWidgets(x).ToList();

                        ApplyLayoutToWidgets(layout, widgets);

                        panel.Widgets = widgets;

                        return panel;
                    });

                return new DashboardConfiguration {Panels = panels};
            }
        }

        private static void ApplyLayoutToWidgets(IEnumerable<IWidgetLayout> layout, IEnumerable<WidgetInstance> widgets)
        {
            var indexedWidgets = widgets.ToDictionary(x => x.Id, StringComparer.OrdinalIgnoreCase);

            foreach (var widgetLayout in layout)
            {
                WidgetInstance widget;

                if (indexedWidgets.TryGetValue(widgetLayout.Id, out widget))
                    widget.InjectFrom(widgetLayout);
            }
        }

        private static DashboardPanelConfiguration LoadPanel(string panelFolder)
        {
            var json = File.ReadAllText(GetPanelFilename(panelFolder));
            var panel = JsonConvert.DeserializeObject<DashboardPanelConfiguration>(json);
            panel.Name = Path.GetFileNameWithoutExtension(panelFolder);
            return panel;
        }

        private static IEnumerable<IWidgetLayout> LoadLayout(string panelFolder)
        {
            var json = File.ReadAllText(GetLayoutFilename(panelFolder));
            return JsonConvert.DeserializeObject<IEnumerable<GridsterWidgetLayout>>(json);
        }

        private static IEnumerable<WidgetInstance> LoadWidgets(string panelFolder)
        {
            var widgetfiles = Directory.GetFiles(GetWidgetsFolder(panelFolder), "*.json", SearchOption.TopDirectoryOnly);

            return widgetfiles.Select(x =>
            {
                var json = File.ReadAllText(x);
                var widget = JsonConvert.DeserializeObject<WidgetInstance>(json);
                widget.Id = Path.GetFileNameWithoutExtension(x);
                return widget;
            });
        }


        private void EstablishConfiguration()
        {
            if (Directory.Exists(GetPanelsFolder()))
                return;

            var myFirstWidget = new WidgetInstance
            {
                Id = "my_first_widget"
            };
            myFirstWidget.UseTemplatePack("message")
                .AddTemplateData(new
                {
                    FontSize = "36px"
                })
                .RenderWith<TemplatedWidgetRenderer>();

            var starterDashboard = new DashboardPanelConfiguration()
                .AddWidget(myFirstWidget);

            var panels = new List<DashboardPanelConfiguration>
            {
                starterDashboard
            };
            var defaultConfig = new DashboardConfiguration
            {
                Panels = panels
            };

            Save(defaultConfig);
        }

        public void Save(DashboardConfiguration configuration)
        {
            lock (_syncLock)
            {
                foreach (var panel in configuration.Panels)
                {
                    var panelFolder = GetPanelFolder(panel);
                    Directory.CreateDirectory(panelFolder);

                    WritePanel(panelFolder, panel);
                    WriteLayout(panelFolder, panel);
                    WriteWidgets(panelFolder, panel);
                }
            }
        }

        private static void WritePanel(string panelFolder, IDashboardPanel panel)
        {
            var config = new
            {
                panel.DwellInSeconds,
                panel.Id,
                panel.Sequence,
                panel.Version
            };
            var json = JsonConvert.SerializeObject(config, Formatting.Indented);

            var filename = GetPanelFilename(panelFolder);
            File.WriteAllText(filename, json);
        }

        private static void WriteLayout(string panelFolder, IDashboardPanel panel)
        {
            var layout = panel.Widgets.Select(x => 
            {
                var item = new GridsterWidgetLayout();
                item.InjectFrom(x);
                return item;
            });
            var json = JsonConvert.SerializeObject(layout, Formatting.Indented);

            var filename = GetLayoutFilename(panelFolder);
            File.WriteAllText(filename, json);
        }

        private static void WriteWidgets(string panelFolder, IDashboardPanel panel)
        {
            foreach (var widget in panel.Widgets
                .Select(x => new
                {
                    Filename = GetWidgetFilename(panelFolder, x),
                    Instance = new
                                {
                                    x.RendererId,
                                    x.Description,
                                    x.CssTemplate,
                                    x.MarkupTemplate,                                    
                                    x.SampleTemplate,
                                    x.ScriptTemplate,
                                    x.TemplateData,
                                    x.TemplatesFolder,
                                    x.Title
                                }
                }))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(widget.Filename) ?? string.Empty);
                var json = JsonConvert.SerializeObject(widget.Instance, Formatting.Indented);

                File.WriteAllText(widget.Filename, json);
            }
        }

        private static string GetWidgetFilename(string panelFolder, IWidgetLayout widget)
        {
            return Path.ChangeExtension(Path.Combine(GetWidgetsFolder(panelFolder), widget.Id), ".json");
        }

        private static string GetLayoutFilename(string panelFolder)
        {
            return Path.Combine(panelFolder, "layout.json");
        }
        private static string GetPanelFilename(string panelFolder)
        {
            return Path.Combine(panelFolder, "panel.json");
        }

        private string GetPanelsFolder()
        {
            return Path.Combine(_folder, "panels");
        }
        private static string GetWidgetsFolder(string panelFolder)
        {
            return Path.Combine(panelFolder, "widgets");
        }

        private string GetPanelFolder(IDashboardPanel panel)
        {
            return Path.Combine(GetPanelsFolder(), panel.Name);
        }
    }
}